/*******************************************************************************
 *
 *    Copyright (c) Dolphin Browser
 *
 *    ViewTest
 *
 *    FlingableLinearLayout
 *    TODO File description or class description.
 *
 *    @author: derron
 *    @since:  2010-3-8
 *    @version: 1.0
 *
 ******************************************************************************/
package com.sdk.widget.threescreen.scrollview;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.LinearLayout;
import android.widget.Scroller;

public class FlingableLinearLayout extends LinearLayout {
    protected static final int VELOCITY_MEASURE_TIME_MS = 1000;
    private OnFlingListener flingListener;
    protected boolean isScrollMode;
    protected int leftScrollMargin;
    protected MotionDetector motionDetector;
    protected MotionDetector.MotionIntent motionIntent;
    private boolean notifyFlingFinished;
    protected int preX;
    protected int rightScrollMargin;
    private OnScrollListener scrollListener;
    protected boolean scrollable;
    private Scroller scroller;
    private OnScrollingListener mOnScrollingListener;

    public FlingableLinearLayout(Context paramContext) {
        super(paramContext);
        this.motionDetector = new MotionDetector();
        init();
    }

    public FlingableLinearLayout(Context paramContext,
            AttributeSet paramAttributeSet) {
        super(paramContext, paramAttributeSet);
        this.motionDetector = new MotionDetector();
        init();
    }

    private void doScrolling(int paramInt) {
        getScrollX();
        int i = -paramInt;
        scrollBy(i, 0);
        invalidate();
    }

    private boolean fling(int minX, int maxX) {
        boolean result = false;
        if (scrollable){
            Log.d(null, "FlingableLinerLayout.fling");
            if(MotionDetector.MotionIntent.HORIZONTAL_SCROLL == motionIntent){
                int x = getVelocityX();
                FlingDirection direction = getFlingDirection(x);
                if(FlingDirection.NONE != direction){
                    int scrollY = getScrollY();
                    isScrollMode = false;
                    scroller.fling(getScrollX(),
                            getScrollY(), -x, 0, minX, maxX, scrollY, scrollY);
                    invalidate();
                    result = true;
                }
            }
        }
        return result;
    }

    private FlingDirection getFlingDirection(int velocityX) {
        FlingDirection direction = FlingDirection.NONE;
        if(MotionDetector.MotionIntent.HORIZONTAL_SCROLL == motionIntent){
            int x = Math.abs(velocityX);
            int minimumFlingVelocity = ViewConfiguration.get(getContext()).getScaledMinimumFlingVelocity();
            if(x > minimumFlingVelocity){
                if(velocityX > 0){
                    direction = FlingDirection.RIGHT;
                }
                else {
                    direction = FlingDirection.LEFT;
                }
            }
        }
        return direction;
    }

    private int getVelocityX() {
        VelocityTracker localVelocityTracker = this.motionDetector
                .getVelocityTracker();
        localVelocityTracker.computeCurrentVelocity(VELOCITY_MEASURE_TIME_MS);
        int i = (int) localVelocityTracker.getXVelocity();
        this.motionDetector.reset();
        return i;
    }

    private void init() {
        this.scroller = new Scroller(getContext());
    }

    protected void scrollInTouch(MotionEvent ev) {
        int i = (int) ev.getX();
        int j = this.preX;
        int k = i - j;
        doScrolling(k);
        this.preX = i;
    }

    protected void stopScroll() {
        if(!scroller.isFinished()){
            scroller.abortAnimation();
        }
    }

    @Override
    public void computeScroll() {
        if (scroller.computeScrollOffset()) {
            notifyFlingFinished = true;
            int i = this.scroller.getCurrX();
            this.scroller.getCurrY();
            int j = getScrollY();
            super.scrollTo(i, j);
            postInvalidate();
        }
        else {
            if(notifyFlingFinished){
                notifyFlingFinished = false;
                if(null != flingListener){
                    flingListener.onFlingFinished(getScrollX(), getScrollY());
                }
            }
        }
    }

    public boolean fling() {
        int i = -this.leftScrollMargin;
        int j = getMeasuredWidth();
        int k = getWidth();
        int l = j - k;
        int i1 = this.rightScrollMargin;
        int i2 = l + i1;
        return fling(i, i2);
    }

    public boolean flingTo(int paramInt) {
        boolean result = false;
        if(scrollable){
//            Log.d(null, "FlingableLinerLayout.flingTo " + paramInt);
            int scrollY = getScrollY();
            int scrollX = getScrollX();
            int dx = paramInt - scrollX;
            this.isScrollMode = true;
            this.scroller.startScroll(scrollX, scrollY, dx, 0);
            invalidate();
            result = true;
        }
        return result;
    }

    public FlingDirection getFlingDirection() {
        int i = getVelocityX();
        return getFlingDirection(i);
    }

    public boolean isScrollable() {
        return this.scrollable;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if(ev.getPointerCount() > 1){
            return super.onInterceptTouchEvent(ev);
        }
        boolean result = false;
        int action = ev.getAction();
        if(MotionEvent.ACTION_DOWN == action){
            if(!isScrollMode){
                stopScroll();
            }
            motionDetector.start(ev);
            motionIntent = MotionDetector.MotionIntent.UNKNOWN;
        }
        else if(MotionEvent.ACTION_MOVE== action){
            if(MotionDetector.MotionIntent.UNKNOWN == motionIntent){
                motionDetector.addMotionEvent(ev);
                motionIntent = motionDetector.detect();
                if(MotionDetector.MotionIntent.HORIZONTAL_SCROLL == motionIntent){
                    preX = (int) ev.getX();
                    result = true;
                }
            }
        }
        else if (MotionEvent.ACTION_UP == action) {
            motionDetector.reset();
        }
        return result;
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r,b);
        int count = getChildCount();
        int totalWidth = 0;
        for (int k = 0; k < count; k++) {
            View view = getChildAt(k);
            int width = view.getWidth();
            totalWidth += width;
        }
        int width = totalWidth + rightScrollMargin + leftScrollMargin;
        boolean canScroll = false;
        if(width > getWidth()){
            canScroll = true;
        }
        scrollable = canScroll;
        if(!scrollable){
            super.scrollTo(-leftScrollMargin, getScrollY());
        }
    }

    @Override
    protected void onScrollChanged(int scrollX, int scrollY, int oldScrollX,
            int oldScrollY) {
        if (null != this.scrollListener){
            scrollListener.onScrollChanged(scrollX, scrollY, oldScrollX, oldScrollY);
        }
        if (null != mOnScrollingListener) {
            mOnScrollingListener.onScrolling(scrollX, scrollY, oldScrollX, oldScrollY);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        boolean result = false;
        if (MotionDetector.MotionIntent.HORIZONTAL_SCROLL == motionIntent) {
            int action = ev.getAction();

            if (MotionEvent.ACTION_MOVE == action) {
                scrollInTouch(ev);
                motionDetector.addMotionEvent(ev);
            } else if (MotionEvent.ACTION_UP == action) {
                motionDetector.reset();
            }
            result = true;
        }
        return result;
    }

    @Override
    public void scrollTo(int x, int y) {
        if (scrollable) {
            int measuredWidth = getMeasuredWidth();
            int width = getWidth();
            measuredWidth -= width;
            measuredWidth += rightScrollMargin;
            int i = leftScrollMargin;
            i = -i;
            if(x < i){
                measuredWidth = -leftScrollMargin;
            }
            else {
                if (x <= measuredWidth) {
                    measuredWidth = x;
                }
            }
            super.scrollTo(measuredWidth, y);
        }
    }

    public void setLeftScrollMargin(int margin) {
        this.leftScrollMargin = margin;
        int i = -margin;
        int j = getScrollY();
        super.scrollTo(i, j);
    }

    public void setOnFlingListener(OnFlingListener paramOnFlingListener) {
        this.flingListener = paramOnFlingListener;
    }

    public void setOnScrollListener(OnScrollListener paramOnScrollListener) {
        this.scrollListener = paramOnScrollListener;
    }

    public void setRightScrollMargin(int paramInt) {
        this.rightScrollMargin = paramInt;
    }

    public static interface OnFlingListener {
        public void onFlingFinished(int scrollX, int scrollY);
    }

    public static interface OnScrollListener {
        public void onScrollChanged(int paramInt1, int paramInt2,
                int paramInt3, int paramInt4);
    }

    public static enum FlingDirection {
        NONE, LEFT, RIGHT,
    }

    public void setOnScrollingListener(OnScrollingListener scrollingListener) {
        mOnScrollingListener = scrollingListener;
    }

    public static interface OnScrollingListener {
        public void onScrolling(int scrollX, int scrollY,
                int oldScrollX, int oldScrollY);
    }
}
